from selenium import webdriver
from selenium.common import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.wait import WebDriverWait

from config.config import *


class Base:
    TIMEOUT = 10
    POLL_FREQUENCY = 0.5

    # 初始化 driver驱动
    def __init__(self, driver=None):
        if driver is None:
            self.driver = webdriver.Edge()
            self.driver.get(SMP_ADDRESS)
            self.driver.maximize_window()
            self.driver.implicitly_wait(1)
        else:
            self.driver = driver

    # 退出浏览器
    def close(self):
        self.driver.quit()

    # 定位单个元素，没找到该元素/超时时，会报错
    def base_find(self, by=By.XPATH, locate=None):
        return WebDriverWait(self.driver, self.TIMEOUT, self.POLL_FREQUENCY).until(
            lambda x: self.driver.find_element(by, locate))

    # 定位多个元素，没找到该元素时，返回0
    def base_finds(self, by=By.XPATH, locate=None):
        if locate is None: raise ValueError("定位表达式（locate）不能为空")
        try:  # 等待条件：至少找到一个元素（列表长度 > 0）
            return WebDriverWait(self.driver, self.TIMEOUT, self.POLL_FREQUENCY).until(
                lambda x: len(self.driver.find_elements(by, locate)) > 0 and self.driver.find_elements(by, locate))
        except TimeoutException:
            if len(self.driver.find_elements(by, locate)) == 0:  # 如果查找的元素为0，则返回空列表，由上层判断
                return []
            else:  # 抛出明确的异常
                raise Exception(f"超时 {self.TIMEOUT} 秒，未找到符合条件的元素（{by}：{locate}）")

    # 点击定位的元素
    def base_click(self, by=By.XPATH, locate=None):
        return self.base_find(by, locate).click()

    # 给指定的元素输入内容
    def base_send(self, by=By.XPATH, locate=None, value=None):
        return self.base_find(by, locate).send_keys(value)

    # 清除输入框中的文本
    def base_send_clear(self, by=By.XPATH, locate=None):
        self.base_find(by, locate).clear()

    # 处理alert弹窗的情况
    def base_alert(self):
        return self.driver.switch_to.alert

    # 点击alert弹窗内的同意
    def base_alert_accept(self):
        try:  # 捕获异常，可能当前页面不存在alert弹窗，无法点击确定
            return self.base_alert().accept()
        except Exception as e:
            print(e)

    # 获取alert弹窗的内容
    def base_alert_text(self):
        return self.base_alert().text

    # 返回上一页
    def base_back(self):
        return self.driver.back()

    # 定位选择框的元素
    def base_find_select(self, by=By.XPATH, locate=None, value=None):
        if locate is None or value is None:
            raise ValueError("参数错误：locate（定位表达式）和 value（选项文本）不能为空")
        select = Select(self.base_find(by, locate))  # 创建Select对象
        select.select_by_visible_text(value)  # 通过 Select 对象选中 value内容

    # 获取第一页的内容。
    def base_get_first_page_device_mode(self, by, locate, item) -> []:  # -> []：表示返回值是一个列表数据
        values = self.base_finds(by, locate)

        deviceModels = []
        for idx, val in enumerate(values):
            if (idx + 1) % item == 0:  # % item：表单内的有item项，每项都要填入内容
                deviceModels.append(
                    [values[idx - 2].text, values[idx - 1].text, values[idx].text])
        return deviceModels

    # 删除第一项表单的内容
    def base_del_first_item(self, by, locate) -> bool:
        delBtn = self.base_finds(by, locate)
        if not delBtn:
            return False
        delBtn[0].click()
        self.base_alert_accept()
        return True

    # 获取定位元素的文本内容
    def base_get_text(self, by, locate):
        return self.base_find(by, locate).text
